import Vue from 'vue';
import Vuex from 'vuex';
import { HttpConfig, HttpConfigTD, CommonRes } from '@/http/types';
import { AxiosResponse } from 'axios';
import { State, Getter, Mutation } from 'vuex-class';
import css from './modules/css';

Vue.use(Vuex);

const getModuleName = (key: string): string => {
  const matches = key.match(/[a-z0-9]*/);
  return matches ? matches[0] : '';
};

interface ApiRequestData {
  config: HttpConfig;
  params?: any;
  data?: any;
}

const store = new Vuex.Store({
  modules: {
    css,
  },
  mutations: {
    setKeyValue(state: any, data: { key: string; value: any }): void {
      const moduleName = getModuleName(data.key);
      if (!state[moduleName]) {
        store.registerModule(moduleName, {
          namespaced: true,
          state: {},
          mutations: {},
          actions: {},
          getters: {},
        });
      }
      Vue.set(state[moduleName], data.key, data.value);
    },
  },
  actions: {
    apiRequest: ({ commit }, payload: ApiRequestData) => {
      const config = payload.config;
      const statusKey = config.name + 'Status';
      commit('setKeyValue', {
        key: statusKey,
        value: true,
      });
      return window.axios
        .request({
          method: config.method,
          url: config.url,
          headers: config.headers,
          params: payload.params,
          data: payload.data,
        })
        .then(
          res => {
            if (res) {
              commit('setKeyValue', {
                key: config.name,
                value: config.transformResponse ? config.transformResponse(res) : res.data,
              });
            }
            commit('setKeyValue', {
              key: statusKey,
              value: false,
            });
            return Promise.resolve(res);
          },
          e => {
            commit('setKeyValue', {
              key: statusKey,
              value: false,
            });
            return Promise.reject(e);
          },
        );
    },
  },
});

export type StoreRequestTd<T> = (data?: T) => Promise<AxiosResponse>;

export function StoreRequest<R>(config: HttpConfigTD<R>): (target: any, propertyKey: string) => any {
  return (target: any, propertyKey: string) => {
    const request: StoreRequestTd<any> = (payload = {}) => {
      const { params, data } = payload;
      const temp = {
        config: new HttpConfig(config),
        params,
        data,
      };
      return store.dispatch('apiRequest', temp);
    };
    target[propertyKey] = request;
  };
}

export interface StoreRequest2Td<T, R> {
  (p: T): Promise<AxiosResponse<CommonRes<R>>>;
  config: HttpConfigTD<R>;
}

export function StoreRequest2<T, R>(config: HttpConfigTD<R>): StoreRequest2Td<T, R> {
  const p = (payload: any) => {
    const temp = {
      config: new HttpConfig(config),
      ...payload,
    };
    return store.dispatch('apiRequest', temp);
  };
  p.config = config;
  return p;
}

export function ApiState(key: string) {
  const name = getModuleName(key);
  if (!store.state[name]) {
    store.registerModule(name, {
      namespaced: true,
      state: {},
      mutations: {},
      actions: {},
      getters: {},
    });
  }
  return State(key, { namespace: name });
}

export { State, Getter, Action } from 'vuex-class';

export default store;
